# /usr/bin/python3
# coding=utf-8
# @Time:2025/9/23 21:48
# @Author: cc
import time

from selenium.common.exceptions import ElementNotVisibleException, WebDriverException, NoSuchElementException, \
    StaleElementReferenceException
from selenium.webdriver.common.keys import Keys

from common.yaml_config import GetConfig
from testcases.test_driver import driver
from selenium.webdriver.common.action_chains import ActionChains


class ObjectMap :
    url = GetConfig ( ).get_url ( )

    def element_get(self, driver, locate_type, locate_expression, must_be_visible=False, timeout=10) :
        # 开始时间
        start_ms = time.time ( ) * 1000
        # 设置结束时间
        stop_ms = start_ms + (timeout * 1000)
        for x in range (int (timeout * 10)) :
            try :
                element = driver.find_element (by=locate_type, value=locate_expression)
                # 如果元素不是必须可见的，就直接返回元素
                if not must_be_visible :
                    return element
                else :
                    # 如果元素是必须可见的，则需要判断元素可见
                    if element.is_displayed ( ) :
                        return element
                    else :
                        raise Exception ( )

            except Exception :
                now_time = time.time ( ) * 1000
                if now_time > stop_ms :
                    break
                pass
            time.sleep (0.1)
        raise ElementNotVisibleException (f"元素定位失败。定位方式为{locate_type},定位表达式为{locate_expression}")

    def wait_for_ready_state_complete(self, driver, timeout=30) :
        '''
        等待页面完全加载完成
        :param driver:浏览器驱动
        :param timeout:超时时间
        :return:
        '''
        # 开始时间
        start_ms = time.time ( ) * 1000
        # 设置结束时间
        stop_ms = start_ms + (timeout * 1000)
        for x in range (int (timeout * 10)) :
            try :
                # 获取页面状态
                ready_state = driver.execute_script ("return document.readyState")
            except WebDriverException :
                # 如果有webdriver错误，就会执行JS失败，跳过处理
                time.sleep (0.03)
                return True
            # 如果页面全部加载完成，返回Ture
            if ready_state == "complete" :
                time.sleep (0.03)
                return True
            else :
                now_ms = time.time ( ) * 1000
                if now_ms >= stop_ms :
                    break
                time.sleep (0.1)
        raise Exception ("打开网页时，页面在%s秒后仍然没有完全加载完" % timeout)

    def element_disappear(self, driver, locate_type, locator_expression, timeout=30) :
        """
        等待页面元素消失
        :param driver: 浏览器驱动
        :param locate_type: 定位方式
        :param locate_expression:定位表达式
        :param timeout: 超时时间
        :return:
        """
        if locate_type :
            # 开始时间
            start_ms = time.time ( ) * 1000
            # 设置结束时间
            stop_ms = start_ms + (timeout * 1000)
            for x in range (int (timeout * 10)) :
                try :
                    element = driver.find_element (by=locate_type, value=locator_expression)
                    # 元素找到，判断元素是否可见
                    if element.is_displayed ( ) :
                        # 元素可见，判断当前时间是否大于结束时间
                        now_ms = time.time ( ) * 1000
                        if now_ms >= stop_ms :
                            break
                        time.sleep (0.1)
                except Exception :
                    return True
                    pass
            raise Exception (f"元素没有消失，定位方式为{locate_type},\n定位表达式为{locator_expression}")
        else :
            pass

    def element_appear(self, driver, locate_type, locator_expression, timeout=30) :
        '''
        等待页面元素出现
        :param driver: 浏览器驱动
        :param locate_type: 定位方式
        :param locator_expression: 定位表达式
        :param timeout: 超时时间
        :return:
        '''
        if locate_type :
            # 开始时间
            start_ms = time.time ( ) * 1000
            # 设置结束时间
            stop_ms = start_ms + (timeout * 1000)
            for x in range (int (timeout * 10)) :
                try :
                    element = driver.find_element (by=locate_type, value=locator_expression)
                    if element.is_displayed ( ) :
                        return element
                    else :
                        raise Exception ( )
                except Exception :
                    now_ms = time.time ( ) * 1000
                    if now_ms >= stop_ms :
                        break
                    time.sleep (0.1)
                    pass
            raise ElementNotVisibleException (
                f"元素没有出现，定位方式为{locate_type},\n定位表达式为{locator_expression}")
        else :
            pass

    def element_to_url(self,
                       driver,
                       url,
                       locate_type_disappear=None,
                       locator_expression_disappear=None,
                       locate_type_appear=None,
                       locator_expression_appear=None,
                       ) :
        '''
        跳转地址
        :param driver: 浏览器驱动
        :param url: 跳转地址
        :param locate_type_disappear: 等待页面元素消失的定位方式
        :param locator_expression_disappear: 等待页面元素消失的定位表达式
        :param locate_type_appear: 等待页面元素出现的定位方式
        :param locator_expression_appear: 等待页面元素出现的定位表达式
        :return:
        '''
        try :
            # 跳转地址
            driver.get (self.url + url)
            # 等待页面元素加载完成
            self.wait_for_ready_state_complete (driver=driver)
            # 跳转地址后等待页面元素消失
            self.element_disappear (driver,
                                    locate_type_disappear,
                                    locator_expression_disappear)
            # 跳转地址后等待页面元素出现
            self.element_appear (driver,
                                 locate_type_appear,
                                 locator_expression_appear)
        except Exception as e :
            print ("跳转地址出现异常，异常的原因为：%s" % e)
            return False
        return True

    def element_is_display(self, driver, locate_type, locator_expression, ) :
        '''
        页面元素是否显示
        :param driver: 浏览器驱动
        :param locate_type: 定位方式
        :param locator_expression: 定位表达式
        :return:
        '''
        try :
            driver.find_element (by=locate_type, value=locator_expression)
            return True
        except NoSuchElementException :
            # 发生了NoSuchElementException，说明在页面上未找到该元素，返回False
            return False

    def element_fill_value(self, driver, locate_type, locator_expression, fill_value, timeout=30) :
        '''
        元素填值
        :param driver: 浏览器驱动
        :param locate_type: 定位方式
        :param locator_expression: 定位表达式
        :param fill_value: 填入的值
        :param timeout: 超时时间
        :return:
        '''
        # 等待元素出现
        element = self.element_appear (driver,
                                       locate_type=locate_type,
                                       locator_expression=locator_expression,
                                       timeout=timeout)
        # 清除输入框中的值
        try :
            element.clear ( )
        except StaleElementReferenceException :
            # 页面没有刷新出来，就对元素进行捕获，从而引发了这个异常
            self.wait_for_ready_state_complete (driver)
            time.sleep (0.1)
            element = self.element_appear (driver,
                                           locate_type=locate_type,
                                           locator_expression=locator_expression,
                                           timeout=timeout)
            try :
                element.clear ( )
            except Exception :
                pass
        except Exception :
            pass
        # 将填入的值转换为字符串
        if type (fill_value) is int or type (fill_value) is float :
            fill_value = str (fill_value)
        # 如果填入的值后面没有跟\n，直接进行填值，不需要回车
        try :
            if not fill_value.endswith ('\n') :
                element.send_keys (fill_value)
                self.wait_for_ready_state_complete (driver=driver)
            else :
                fill_value = fill_value[:-1]
                element.send_keys (fill_value)
                element.send_keys (Keys.RETURN)
                self.wait_for_ready_state_complete (driver=driver)
        except StaleElementReferenceException :
            self.wait_for_ready_state_complete (driver=driver)
            time.sleep (0.1)
            element = self.element_appear (driver,
                                           locate_type=locate_type,
                                           locator_expression=locator_expression,
                                           timeout=timeout)
            element.clear ( )
            if not fill_value.endswith ('\n') :
                element.send_keys (fill_value)
                self.wait_for_ready_state_complete (driver=driver)
            else :
                fill_value = fill_value[:-1]
                element.send_keys (fill_value)
                element.send_keys (Keys.RETURN)
                self.wait_for_ready_state_complete (driver=driver)
        except Exception :
            return Exception ("元素填值失败")
        return True

    def element_click(self,
                      driver,
                      locate_type,
                      locator_expression,
                      locate_type_disappear=None,
                      locator_expression_disappear=None,
                      locate_type_appear=None,
                      locator_expression_appear=None,
                      timeout=30
                      ) :
        '''
        元素点击
        :param driver: 浏览器驱动
        :param locate_type: 定位方式类型
        :param locator_expression: 定位表达式
        :param locate_type_disappear: 元素消失的定位方式
        :param locator_expression_disappear: 元素消失的定位表达式
        :param locate_type_appear: 元素出现的定位方式
        :param locator_expression_appear: 元素出现的定位表达式
        :param timeout: 超时时间
        :return:
        '''
        # 判断元素是否出现
        element = self.element_appear (driver,
                                       locate_type=locate_type,
                                       locator_expression=locator_expression,
                                       timeout=timeout)
        try :
            # 对元素进行点击操作
            element.click ( )
        except StaleElementReferenceException :
            self.wait_for_ready_state_complete (driver=driver)
            time.sleep (0.1)
            element = self.element_appear (driver,
                                           locate_type=locate_type,
                                           locator_expression=locator_expression,
                                           timeout=timeout)
            element.click ( )
        except Exception as e :
            print ("页面出现异常，元素不可点击", e)
            return False
        try :
            self.element_appear (driver,
                                 locate_type_appear,
                                 locator_expression_appear,
                                 timeout)
            self.element_disappear (driver,
                                    locate_type_disappear,
                                    locator_expression_disappear,
                                    timeout)
        except Exception as e :
            print ("等待页面元素出现或消失失败", e)
            return False
        return True

    def upload(self, driver, locate_type, locator_expression, file_path) :
        '''
        文件上传
        :param driver:
        :param locate_type:
        :param locator_expression:
        :return:
        '''

        element = self.element_get (driver, locate_type, locator_expression)
        return element.send_keys (file_path)

    def switch_into_iframe(self, driver, locate_iframe_type, locator_iframe_expression) :
        '''
        进入iframe
        :param driver: 浏览器驱动
        :param locate_iframe_type: 定位iframe的方式
        :return:
        '''
        iframe = self.element_get (driver, locate_iframe_type, locator_iframe_expression)
        driver.switch_to.frame (iframe)

    def switch_from_iframe_to_content(self, driver) :
        '''
        从iframe切回主文档
        :param driver:
        :return:
        '''
        driver.switch_to.parent_frame ( )

    def switch_window_2_latest_handle(self, driver) :
        '''
        句柄切换窗口到最新的窗口
        :param driver:
        :return:
        '''
        # 获取当前所有的窗口列表（句柄）
        window_handles = driver.window_handles
        driver.switch_to_window (window_handles[-1])

    def suspended(self, driver, locate_type, locator_expression) :
        '''
        悬浮操作-点击
        :param driver:
        :return:
        '''
        actions = ActionChains (driver)
        element = self.element_get (driver, locate_type, locator_expression)
        # 执行悬浮操作
        actions.move_to_element (element).perform ( )
